#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <dirent.h>

/*
 * readdir()函数返回一个指向目录结构的指针，该结构表示目录流中由p_dir指向的下一个目录条目。
 * 当到达目录流的末尾或发生错误时，它将返回NULL。
 */

#if 0 

struct dirent *readdir(DIR * p_dir);

struct dirent {
	ino_t          d_ino;       /* Inode number */
	off_t          d_off;       /* Not an offset; see below */
	unsigned short d_reclen;    /* Length of this record */
	unsigned char  d_type;      /* Type of file; not supported
								  by all filesystem types */
	char           d_name[256]; /* Null-terminated filename */
};

struct dirent {
      ino_t      d_ino;   // 文件的inode
      off_t      d_off;   //  该文件相对于文件夹的偏移量
      unsigned short d_reclen;    // d_name的长度
      unsigned char  d_type;   //  文件类型, 例如管道, Socket , Block等
      char      d_name[256]; // 文件名
  };

The only fields in the dirent structure that are mandated by
       POSIX.1 are d_name and d_ino.  The other fields are
       unstandardized, and not present on all systems; see NOTES below
       for some further details.

       The fields of the dirent structure are as follows:

       d_ino  This is the inode number of the file.

       d_off  The value returned in d_off is the same as would be
              returned by calling telldir(3) at the current position in
              the directory stream.  Be aware that despite its type and
              name, the d_off field is seldom any kind of directory
              offset on modern filesystems.  Applications should treat
              this field as an opaque value, making no assumptions about
              its contents; see also telldir(3).

       d_reclen
              This is the size (in bytes) of the returned record.  This
              may not match the size of the structure definition shown
              above; see NOTES.

       d_type This field contains a value indicating the file type,
              making it possible to avoid the expense of calling
              lstat(2) if further actions depend on the type of the
              file.

              When a suitable feature test macro is defined
              (_DEFAULT_SOURCE on glibc versions since 2.19, or
              _BSD_SOURCE on glibc versions 2.19 and earlier), glibc
              defines the following macro constants for the value
              returned in d_type:

              DT_BLK This is a block device.

              DT_CHR This is a character device.

              DT_DIR This is a directory.

              DT_FIFO
                     This is a named pipe (FIFO).

              DT_LNK This is a symbolic link.

              DT_REG This is a regular file.

              DT_SOCK
                     This is a UNIX domain socket.

              DT_UNKNOWN
                     The file type could not be determined.

              Currently, only some filesystems (among them: Btrfs, ext2,
              ext3, and ext4) have full support for returning the file
              type in d_type.  All applications must properly handle a
              return of DT_UNKNOWN.

       d_name This field contains the null terminated filename.  See
              NOTES.

       The data returned by readdir() may be overwritten by subsequent
       calls to readdir() for the same directory stream.
返回值顶部
       On success, readdir() returns a pointer to a dirent structure.
       (This structure may be statically allocated; do not attempt to
       free(3) it.)

       If the end of the directory stream is reached, NULL is returned
       and errno is not changed.  If an error occurs, NULL is returned
       and errno is set to indicate the error.  To distinguish end of
       stream from an error, set errno to zero before calling readdir()
       and then check the value of errno if NULL is returned.
错误顶部
       EBADF  Invalid directory stream descriptor p_dir.
属性顶部
       For an explanation of the terms used in this section, see
       attributes(7).

       ┌─────────────────────┬───────────────┬──────────────────────────┐
       │Interface            │ Attribute     │ Value                    │
       ├─────────────────────┼───────────────┼──────────────────────────┤
       │readdir()            │ Thread safety │ MT-Unsafe race:dirstream │
       └─────────────────────┴───────────────┴──────────────────────────┘


#endif

struct direnttt {
      ino_t      d_ino;   // 文件的inode
      off_t      d_off;   //  该文件相对于文件夹的偏移量
      unsigned short d_reclen;    // d_name的长度
      unsigned char  d_type;   //  文件类型, 例如管道, Socket , Block等
      //char      d_name[256]; // 文件名
  };

static int test() 
{
	DIR * p_dir = NULL;
	struct dirent * dp = NULL;
	int len = -1;
	char *name = "aaaaa";
	char *ptr;

	// 通过opendir打开目录
	p_dir = opendir("/proc");
	if (p_dir == NULL) {
	   return (-1);
	}

	len = strlen(name);

	//  开始遍历p_dir文件夹
	while ((dp = readdir(p_dir)) != NULL) {	 
		printf("inode number:%d offset:%d len:%d\n", dp->d_ino, dp->d_off, dp->d_reclen);
		printf("name:%s:%d\n", dp->d_name, dp->d_type);

		//  比较dp->d_name与name的值, 如果匹配则返回
		if (dp->d_reclen == len && strcmp(dp->d_name, name) == 0) {
			(void)closedir(p_dir);
			return 0;
		}

		if (strtol(dp->d_name, &ptr, 0xa) != 0)
			break;
	}

	printf("sizeof(struct dirent)=%d, -name=%d\n", sizeof(struct dirent), sizeof(struct dirent) - 256);
	printf("sizeof(ino_t)=%d, sizeof(off_t)=%d\n", sizeof(ino_t), sizeof(off_t));

	printf("sizeof(struct direnttt)=%d\n", sizeof(struct direnttt));

	//  关闭dir
	(void)closedir(p_dir);

	return 0;
}


int main()
{
	test();

	return 0;
}


// https://www.man7.org/linux/man-pages/man3/readdir.3.html

